package cn.slb.im.modules.chat.webscoket;

import cn.slb.im.modules.chat.dto.ChatMessageDTO;
import cn.slb.im.modules.chat.service.ChatService;
import cn.slb.im.modules.sys.controller.AbstractController;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * socket 连接层
 * sessionId 可以为用户id，也可以为 groupId
 *
 * @author nick
 */
@ServerEndpoint("/chat/{userId}")
@Component
@Slf4j
public class WebSocket extends AbstractController {

    @Autowired
    private ChatService chatService;

    /**
     * String : 用户ID
     * Session： 回话
     */
    public static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();

    /**
     * 连接成功后的回调函数 , 创建会话
     * onOpen是当用户发起连接时，会生成一个用户的Session 注意此Session是 javax.websocket.Session;
     * 然后我们用userId作为Key Session作为Vaule存入Map中暂存起来
     *
     * @param userId
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("userId") String userId, Session session) {
        log.info("WebSocket onOpen userId : {} ", userId);
        if (sessionMap == null) {
            sessionMap = new ConcurrentHashMap<String, Session>();
        }
        sessionMap.put(userId, session);
    }

    /**
     * onClose 是用户关闭聊天窗时，将用户session移除
     *
     * @param userId
     */
    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        log.info("====>WebSocket OnClose userId : {}" , userId);
        sessionMap.remove(userId);
    }

    /**
     * onMessage 实现聊天功能
     * message是前端传来的 JSON字符串。其中含有 ChatMessageDTO 里的信息。根据 dto 实现 点对点/广播聊天。
     * @param messageStr
     */
    @OnMessage
    public void onMessage(String messageStr) {
        log.info("====>WebSocket OnMessage message : {}" , messageStr);
        // 1 将 message 转换为 实体
        ChatMessageDTO dto = JSON.parseObject(messageStr, ChatMessageDTO.class);
        // 2 判断是群聊、还是单聊
        if(0 == dto.getChatType()){
            // 单聊
            toOne(dto);
        }else if (1 == dto.getChatType()){
            // 群聊
            toAll(dto);
        }else {
            log.error("WebSocket OnMessage is exception , exception is : unKnow chat type");
            throw new RuntimeException("chat type unKnow");
        }
    }



    /**
     * 当出现异常时候自动调用该方法。
     *
     * @param t
     */
    @OnError
    public void error(Throwable t) {
        log.error("WebSocket OnError error : {} , ",t.getMessage() , t);
        t.printStackTrace();
    }

    /**
     * 点对点
     * session.getAsyncRemote().sendText(message); 即向目标session发送消息。
     */
    private void toOne(ChatMessageDTO dto) {
        // 获取消费者的 session
        Session consumerSession = sessionMap.get(dto.getToId());
        if (consumerSession == null) {
            log.error("消息消费者不存在");
        } else {
            consumerSession.getAsyncRemote().sendText(dto.getMsg());
        }
        // 3 保存消息
        chatService.saveChat(dto);
    }


    /**
     * 广播
     * 向所有session发送消息
     */
    private void toAll(ChatMessageDTO dto) {
        // 1 获取群聊ID
        String toId = dto.getToId();
        // 2 TODO 根据群聊ID获取，用户
        List<String> userIds = new ArrayList<>();
        // 3 遍历用户ID集合，发送消息
        userIds.forEach(user->{
            Session session = sessionMap.get(user);
            session.getAsyncRemote().sendText(dto.getMsg());
        });
    }
}
